#ifndef __MY_SERVICE__
#define __MY_SERVICE__

#include "dataManager.hpp"
#include "./mylib/include/httplib.h"
#include "dataBaseManager.hpp"
#include <memory>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <string>

// 业务处理的回调函数使用的是httplib封装好的Get/Post
// 传入的第二个参数是只能是req+rsp的函数指针 所以回调函数只能设为静态函数
// 因为非静态成员函数第一个参数默认是this指针
// 同样的 dataManager也无法入参 ⽆法直接访问外部的数据管理模块数据
// 将数据管理模块的对象定义为全局数据 在此处声明⼀下 在别处定义 在该文件任意位置都可以访问

// 在多个文件中使用 _dataManager 确保它在每个文件中都被声明为extern 但只在一个源文件中被定义
extern std::shared_ptr<cloudBackup::DataManager> _dataManager;

namespace cloudBackup
{
	class Service
	{
	private:
		/*
		struct MultipartFormData
		{
			std::string name;
			std::string content;
			std::string filename;
			std::string content_type;
		};
		*/
		int _serverPort;
		std::string _serverIp;
		std::string _urlDownloadID; // url请求 /download/ 表示下载请求
		httplib::Server _server;

	public:
		Service()
		{
			Config *config = Config::GetInstance();
			_serverPort = config->GetServerPort();
			_serverIp = config->GetServerIp();
			_urlDownloadID = config->GetUrlDownloadID();
		}

		bool RunServiceModule()
		{
			// /upload/usrID/fileEtag
			_server.Post(R"(/upload/(\d+)/([a-zA-Z0-9._-]+))", Upload);
			// _server.Post(R"(.*)", Upload);
			// _server.Post(R"(^/upload/\d+/\d+-[a-zA-Z0-9%.-]+-\d+-\d+$)", Upload);

			// /showfile/usrID/password
			_server.Get(R"(/showfile/(\d+)/([a-zA-Z0-9._-]+)/)", ShowFile);
			_server.Get(R"(/showfile/(\d+)/([a-zA-Z0-9._-]+))", ShowFile);

			_server.Get("/showfile/(\\d+)/webPage/bgimg.jpg", WebBgImg);
			_server.Get("/showfile/(\\d+)/webPage/bgmusic.mp3", WebBgMusic);
			std::string downloadReq = _urlDownloadID + R"(.*)"; // /download/test.txt
			_server.Get(downloadReq, Download);

			_server.listen(_serverIp.c_str(), _serverPort);

			usleep(1000);
			return true;
		}

	private:
		//_server.Get("/showfile/1/webPage/bgimg.jpg", WebBgImg);
		static void WebBgImg(const httplib::Request &req, httplib::Response &rsp)
		{
			std::stringstream ss;
			std::ifstream ifs("./webPage/bgimg.jpg", std::ios::binary);
			if (ifs.is_open() == false)
			{
				Log::log(LOGERROR, "Service::WebResrc::ifs.is_open() failed !: %s: %d", strerror(errno), errno);
				return;
			}
			std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
			ss << content;
			ifs.close();

			rsp.body = ss.str();
			rsp.set_header("Content-Type", "application/octet-stream");
			rsp.status = 200;
			return;
		}

		//_server.Get("/showfile/1/webPage/bgmusic.mp3", WebBgMusic);
		static void WebBgMusic(const httplib::Request &req, httplib::Response &rsp)
		{
			std::stringstream ss;
			std::ifstream ifs("./webPage/bgmusic.mp3", std::ios::binary);
			if (ifs.is_open() == false)
			{
				Log::log(LOGERROR, "Service::WebResrc::ifs.is_open() failed !: %s: %d", strerror(errno), errno);
				return;
			}
			std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
			ss << content;
			ifs.close();

			rsp.body = ss.str();
			rsp.set_header("Content-Type", "audio/mp3");
			rsp.status = 200;
			return;
		}

		// 处理客户端发起的upload请求  /upload/usrID/fileEtag
		static void Upload(const httplib::Request &req, httplib::Response &rsp)
		{
			// post /upload HTTP/1.1 文件数据在正文中 正文并不全是文件数据

			// 1.判断req有没有name为file的文件区域
			//  通过客户端发送的MultipartFormData::name字段 判断有没有name为file的文件区域
			auto isHaveFile = req.has_file("file");
			if (isHaveFile == false)
			{
				Log::log(LOGERROR, "Service::Upload::isHaveFile failed !: %s: %d", strerror(errno), errno);
				rsp.status = 400; // 有upload请求但没有文件 函数结束
				return;
			}
			Log::log(INFO, "Service::Upload::isHaveFile success!: %s: %d", strerror(errno), errno);

			// 2. 获取整个MultipartFormData结构体 构建新路径存放uploadFile
			//  通过客户端发送的MultipartFormData::name字段 获取整个MultipartFormData结构体
			const auto &MultipartObj = req.get_file_value("file");
			std::string backupDir = Config::GetInstance()->GetBackupDir(); // 获取配置文件中的备份文件路径

			// 解码文件名
			// std::string decodeFileName = FileUtil::UrlDecode(MultipartObj.filename);
			std::string backupPath = backupDir + FileUtil(MultipartObj.filename).FileName(); // 构建备份路径存放收到的文件
			Log::log(INFO, "Service::Upload::backupPath: %s", backupPath.c_str());

			// 3.将收到的文件的数据写入到备份文件中
			FileUtil fileUtil(backupPath); //./backupDir/test.txt
			fileUtil.SetContent(MultipartObj.content);

			// 4. 记录新文件的属性信息 添加到数据管理模块
			auto usrID_str = req.matches[1].str();
			// auto fileEtag_str = FileUtil::UrlDecode(req.matches[2].str());
			auto fileEtag_str = req.matches[2].str();
			uint32_t fileId;
			uint32_t userId = std::stoi(usrID_str);

			Log::log(INFO, "recved file etag: %s", fileEtag_str.c_str()); // debug

			cloudBackup::DataBaseManager &dbManager = cloudBackup::DataBaseManager::getDBInstance();
			dbManager.getFileIdByFEtag(fileId, fileEtag_str);

			BackupAInfo info(fileId, userId, backupPath); //./backupDir/test.txt
			_dataManager->Insert_Table(info);

			Log::log(INFO, "Service::Uplaod %s upload success!", FileUtil(MultipartObj.filename).FileName().c_str());

			return;
		}

		// /showfile/usrID/password 处理客户端发起的ShowFile请求
		static void ShowFile(const httplib::Request &req, httplib::Response &rsp)
		{
			auto usrID_str = req.matches[1]; // 0保存整体path 往后下标中保存捕捉的数据
			uint32_t userId = std::stoi(usrID_str.str());
			auto req_passwd = req.matches[2];
			std::string usr_passwd = req_passwd.str();
			std::string passwd;
			cloudBackup::DataBaseManager &dbManager = cloudBackup::DataBaseManager::getDBInstance();
			dbManager.getPasswdByUsrId(userId, passwd);
			if (passwd != usr_passwd)
			{
				Log::log(LOGERROR, "Service::ShowFile usr-passwd not match!");
				return;
			}
			// 1. 从哈希表中获取该用户所有文件的属性信息
			std::vector<BackupAInfo> fileArray;
			_dataManager->GetAllBackupAInfo(&fileArray, std::stoi(usrID_str.str()));

			// 2. 根据所有属性信息 组织html文件数据
			std::stringstream ss;

			// 设置head+css文件内容到string对象
			std::ifstream headHtml("./webPage/htmlHead.txt", std::ios::binary);
			if (headHtml.is_open() == false)
			{
				Log::log(LOGERROR, "Service::ShowFile::headHtml.is_open() failed !: %s: %d", strerror(errno), errno);
				return;
			}
			std::string content1((std::istreambuf_iterator<char>(headHtml)), std::istreambuf_iterator<char>());
			ss << content1;
			headHtml.close();

			// 设置bodyBegin数据到str
			std::ifstream bodyBegin("./webPage/bodyBegin.txt", std::ios::binary);
			if (bodyBegin.is_open() == false)
			{
				Log::log(LOGERROR, "Service::ShowFile::bodyBegin.is_open() failed !: %s: %d", strerror(errno), errno);
				return;
			}
			std::string content2((std::istreambuf_iterator<char>(bodyBegin)), std::istreambuf_iterator<char>());
			ss << content2;
			bodyBegin.close();

			// 设置动态数据
			for (auto &perFile : fileArray)
			{
				FileUtil fileUtil(perFile._reqDownloadPath);

				Config *config = Config::GetInstance();
				std::string tmpZipPath = config->GetZipDir() + fileUtil.FileName() + config->GetZipSuffix();
				std::string tmpOriPath = config->GetBackupDir() + fileUtil.FileName();
				FileUtil fuZip(tmpZipPath);
				FileUtil fuOri(tmpOriPath);

				if (fuOri.Exists() || fuZip.Exists())
				{
					ss << "<tr>";
					std::string filename = FileUtil(perFile._backupPath).FileName();
					ss << "<td><a href='" << perFile._reqDownloadPath << "'>" << filename << "</a></td>"; // ./download/a.txt
					ss << "<td class='file-info'" << ">" << TimetoStr(perFile._mtime) << "</td>";
					ss << "<td class='file-info'" << ">" << ShowFileSize(perFile._fsize) << "</td>";
					ss << "</tr>";
				}
			}

			// 设置body末尾+js数据到str
			std::ifstream bodyEnd("./webPage/bodyEnd.txt", std::ios::binary);
			if (bodyEnd.is_open() == false)
			{
				Log::log(LOGERROR, "Service::ShowFile::bodyEnd.is_open() failed !: %s: %d", strerror(errno), errno);
				return;
			}
			std::string content3((std::istreambuf_iterator<char>(bodyEnd)), std::istreambuf_iterator<char>());
			ss << content3;
			bodyEnd.close();

			// 组织http响应
			rsp.body = ss.str();
			rsp.set_header("Content-Type", "text/html");
			rsp.status = 200;

			Log::log(INFO, "Service::ShowFile deal success!");
			return;
		}

		// 处理客户端发起的Download请求 用户点击html中的超链接会发起该请求
		static void Download(const httplib::Request &req, httplib::Response &rsp)
		{
			// 1. 根据客户端请求的资源路径req.path 获取文件属性备份信息
			BackupAInfo info;
			// http://ip:port/path?id=5&num=4#ch
			_dataManager->GetOneByReqDownloadPath(req.path, &info);

			// 2. 判断文件是否被压缩 如果被压缩需要解压缩
			if (info._isZipped == true)
			{
				// 3. 将文件解压到备份目录下
				FileUtil fileUtil(info._zipPath);
				fileUtil.DeCompress(info._backupPath);

				// 4. 删除压缩包 修改备份信息
				fileUtil.Remove();
				info._isZipped = 0;
				_dataManager->Update_Table(info);
			}

			// 5. 判断是否需要断点续传：上次请求的文件etag与本次请求的文件etag是否相同
			bool resume = false;
			std::string old_etag;
			if (req.has_header("If-Range"))
			{
				old_etag = req.get_header_value("If-Range");

				// 请求包含If-Range字段(上次请求文件的etag) 该字段值与客户端本次请求的文件etag相同
				if (old_etag == GetETag(info))
					resume = true;
			}

			// 4. 读取文件数据 放入rsp.body中
			FileUtil fileUtil(info._backupPath);
			if (resume == false)
			{
				fileUtil.GetContent(&rsp.body);

				// 5. 设置响应头部字段： ETag  Accept-Ranges: bytes
				rsp.set_header("Accept-Ranges", "bytes");
				rsp.set_header("ETag", GetETag(info));
				// 资源的MIME类型为通用二进制文件传输
				rsp.set_header("Content-Type", "application/octet-stream");
				rsp.status = 200;
			}
			else
			{
				// httplib内部实现了对于区间请求/断点续传请求的处理
				// 使用者只需将文件所有数据读取到rsp.body中 httplib会判断是否需要断点续传
				// 需要时 它根据请求字段range 从body中取出指定区间数据进行响应

				// 如果httplib没有支持该功能 则需要我们获取range值 调用GetPosLen() 设置区间数据到body
				// std::string range = req.get_header_val("Range"); bytes=start-end
				fileUtil.GetContent(&rsp.body);
				rsp.set_header("Accept-Ranges", "bytes");
				rsp.set_header("ETag", GetETag(info));
				rsp.set_header("Content-Type", "application/octet-stream");
				// rsp.set_header("Content-Range", "bytes start-end/fsize"); httplib已处理
				// rsp.status = 206; // 区间请求响应状态码206 httplib已处理
			}
			Log::log(INFO, "Service::download %s deal success!", req.path.c_str());
			return;
		}

		// 根据文件字节数转为 B/KB/MB
		static std::string ShowFileSize(size_t bytes)
		{
			// std::fixed: 设置浮点数的输出格式为固定的小数点表示法而不是默认的科学记数法
			// std::setprecision(2)：设置浮点数显示的小数位数
			std::ostringstream oss;
			if (bytes == 0)
				return "0B";
			else if (bytes < 1024)
				oss << bytes << "B";
			else if (bytes < 1024 * 1024)
				oss << std::fixed << std::setprecision(2) << (bytes / 1024.0) << "KB";
			else if (bytes < 1024 * 1024 * 1024)
				oss << std::fixed << std::setprecision(2) << (bytes / (1024.0 * 1024.0)) << "MB";
			else
				oss << std::fixed << std::setprecision(2) << (bytes / (1024.0 * 1024.0 * 1024.0)) << "GB";
			return oss.str();
		}

		// 将数字时间戳转换为字符串Sat Aug 17 10:57:21 2024
		static std::string TimetoStr(time_t t)
		{
			char *ctime_str = std::ctime(&t);
			if (ctime_str == nullptr)
			{
				Log::log(LOGERROR, "Service::TimetoStr::Invalid time_t !: %s: %d", strerror(errno), errno);
				return "Invalid time";
			}
			std::string tmp(ctime_str);
			return tmp;
		}

		// 通过BackupAInfo 构建ETag
		static std::string GetETag(const BackupAInfo &info)
		{
			// filename-fsize-mtime
			FileUtil fileUtil(info._backupPath);
			std::string etag = fileUtil.FileName();
			etag += "-";
			etag += std::to_string(info._fsize);
			etag += "-";
			etag += std::to_string(info._mtime);
			return etag;
		}
	};
}

#endif
